package wili.demo.misc;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;

import wili.c.Ca;
import wili.c.Misc9a;

/**
 * @author weila 2020年1月26日
 */
class Stream_demo {
	static class User {
		String id;

		String group;

		int age;

		public String toString() {
			return id + ":" + age;
		}

		public User(String id, String grp, int age) {
			this.id = id;
			this.group = grp;
			this.age = age;
		}

		public int getAge() {
			return age;
		}

		public String getId() {
			return id;
		}

		public String getGroup() {
			return group;
		}
	}

	static void log(int opt, Object msg) {
		if (opt < 0)
			return;
		System.out.println(msg);
	}

	static void flatMap() {//先映射后扁平化
		{//map针对每个元素做转换; flatMap针对集中每个元素e做转换并支持e对其它集合元素的轮询，用结果集替换集合
			List<String> list = Arrays.asList("tom", "jame", "jerry", "hello");
			{
				Stream<String> stream = list.stream();
				Stream<String[]> ssStream = stream.map(s -> s.split(""));//map针对每个元素做转换
				Stream<String> map = ssStream.flatMap(Arrays::stream);
				List<String> lis = map.collect(Collectors.toList());
				System.out.println("sList1=" + lis.size() + lis);
			}
			{
				Stream<String> stream = list.stream();
				Stream<String[]> ssStream = stream.map(s -> s.split(""));
				;
				Stream<String> sStream = ssStream.map(bb -> Stream.of(bb).collect(Collectors.joining()));
				List<String> lis = sStream.collect(Collectors.toList());
				System.out.println("sList2=" + lis.size() + lis);//复原，【上一层元素个数不变，元素变】。
			}
			{//flatMap针对list(nn1的副本）中每个元素做转换，递归配合nn2中的每个元素, 结果集替换list的内容，使其元素和个数都发生变化。
				List<Integer> nn1 = Arrays.asList(1, 2, 3);
				List<Integer> nn2 = Arrays.asList(3, 4);
				List<int[]> pairs = nn1.stream().flatMap(x -> nn2.stream().map(y -> new int[] { x, y }))
						.collect(Collectors.toList());
				System.out.println("\nintsList=");
				for (int[] pair : pairs) {
					System.out.println(Arrays.toString(pair));
				}
			}
			{//peek: 返回stream, 使其在get时，执行插入的动作，类似于对get进行监听
				System.out.println(">>> test peek():");
				List<Integer> lis = Arrays.asList(11, 22, 33);
				Stream<Integer> lis2 = lis.stream().peek(new Consumer<Integer>() {
					@Override
					public void accept(Integer t) {
						System.out.println(" accept:" + t);
					}
				});
				System.out.println("list2:");
				System.out.println(lis2.collect(Collectors.toList()));
			}
		}
	}

	static void collect_test(int use) {
		if (use < 0)
			return;
		{
			log(1, "8---------stringStream:");
			//Stream<String> stream = "englihs is worst saying\n\rdsaf.".lines();
			List<String> lis = Arrays.asList("tom", "jame", "jerry", "hello");
			Stream<String> stream = lis.stream();
			//...用new来装元素，用add来加元素，用addAll来对多个new中的元素进行合并
			List<String> asList = stream.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
			log(1, " asList1=" + asList.size() + asList);
			List<String> asList2 = lis.stream().collect(Collectors.toList());
			log(1, " asList2=" + asList2.size() + asList2);
		}
	}

	static void misc_test(int use) {
		if (use < 0)
			return;
		{
			log(1, "9---------连乘:");
			String[] lis = { "tom", "jame" };
			//始于100，进行规约。a是上次计算结果，b依次是集中的元素。
			int ret = Stream.of(lis).mapToInt(String::length).reduce(100, (a, b) -> a * b);
			System.out.println(" mutils=" + ret);
		}
		{
			int[] nn = IntStream.of(8, 22, 10, 100, 24, 50, 50)//
					.filter((e) -> e > 20)//
					.map((e) -> e * 2)//
					.distinct()//
					.sorted()//
					.toArray();//
			System.out.println("ints=" + Arrays.toString(nn));
		}
		{
			String[] arr = { "aa", "ccc", "sss" };
			System.out.println(Arrays.stream(arr).collect(Collectors.joining()));
			// aacccsss
			System.out.println(Arrays.stream(arr).collect(Collectors.joining("|")));
			// aa|ccc|sss
			System.out.println(Arrays.stream(arr).collect(Collectors.joining(",", "{", "}")));
			// {aa,ccc,sss}
		}
	}

	/**
	 * Stream:: anyMatch, allMatch, count, concat, collect findFirst, findAny,
	 * filter, flatMap, foreach, min, max, map, distinct, sorted, limit, skip, of,
	 * reduce, toArray,
	 */
	static void groupingBy_test(int use) {
		if (use < 0)
			return;
		/* https://zhuanlan.zhihu.com/p/107737318
		Collectors 是 Java 8 加入的操作类，位于 java.util.stream 包下。
		它会根据不同的策略将元素收集归纳起来，比如最简单常用的是将元素装入Map、Set、List 等可变容器中。
		 */
		{
			List<Integer> nums = Arrays.asList(11, 22, 33, 44);
			nums.stream().collect(Collectors.summingInt(Integer::intValue));
		}
		List<User> users = new ArrayList<>(Arrays.asList(new User("j", "z", 11), new User("b", "g1", 55),
				new User("c", "g2", 33), new User("d", "g3", 22), new User("d", "g2", 22)));
		{
			Integer sum = users.stream().collect(Collectors.summingInt(User::getAge));
			int sum2 = users.stream().mapToInt(User::getAge).reduce(0, (a, b) -> a + b);
			Ca.asert(sum == sum2, sum);
			log(1, "1---------\n" + sum + ", sum2=" + sum2);
		}
		{
			List<User> lis2 = users.parallelStream().filter(p -> p.getAge() < 20).collect(Collectors.toList());
			log(1, "2---------\n" + lis2.size() + lis2.toString());
			log(1, "3---------\n");
		}
		{
			Map<Integer, List<User>> map = users.stream().collect(Collectors.groupingBy(User::getAge));
			map.forEach((key, value) -> {
				log(1, key + " ~>" + value);
			});
		}
		{
			Map<String, List<User>> mapByGroup = users.stream()//
					.collect(Collectors.groupingBy(User::getGroup));
			//按group分组.
			log(1, "5.1---------\n" + mapByGroup);
			Comparator<User> oldest = Comparator.comparing(User::getAge);
			//按group分组，各组List中取有最大age的用户.
			Map<String, Optional<User>> mapByGroup_maxAge = users.stream()//
					.collect(Collectors.groupingBy(User::getGroup, Collectors.reducing(BinaryOperator.maxBy(oldest))));
			log(1, "5---------\n" + mapByGroup_maxAge);
		}
		{
			log(1, "6---------\n");
			//按group分组，各组用Set装入，元素取id值,
			Map<String, Set<String>> map = users.stream().collect(//
					Collectors.groupingBy(User::getGroup, Collectors.mapping(User::getId, Collectors.toSet())));
			System.out.println(" map :" + map);
			Map<String, Set<String>> map2 = users.stream().collect(//
					Collectors.groupingBy(User::getGroup, TreeMap::new,
							Collectors.mapping(User::getId, Collectors.toSet())));
			System.out.println(" map2:" + map2);
			System.out.println();
		}
		List<Person> persons = Arrays.asList(new Person("1", "aa", 23), new Person("1", "bb", 13),
				new Person("3", "cc", 14));
		{
			log(1, "7---------");
			Stream<Person> stream = persons.stream();
			//groupingBy
			Map<String, List<Person>> tempMap = persons.stream().collect(Collectors.groupingBy(x -> x.id));
			for (String s : tempMap.keySet()) {
				List<Person> lis = tempMap.get(s);
				System.out.println("\t" + lis);
			}
			Map<Boolean, List<Integer>> collectGroup = Stream.of(1, 2, 3, 4)
					.collect(Collectors.groupingBy(it -> it > 3));
			log(1, "7a---------");
			for (Boolean s : collectGroup.keySet()) {
				List<Integer> lis = collectGroup.get(s);
				System.out.println("\t" + lis);
			}
			System.out.println("collectGroup : " + collectGroup);
		}
		{//多级分组
			log(1, "8---------");
			persons.get(0).type = "police";
			String[] tt = { "police", "killer" };
			int[] inds = { -1 };
			persons.forEach(ele -> ele.type = tt[++inds[0] >= tt.length ? inds[0] = 0 : inds[0]]);
			Map<String, Map<String, List<Person>>> personMap = persons.stream()
					.collect(Collectors.groupingBy(Person::getType, Collectors.groupingBy(item -> {
						if (item.getAge() < 18) {
							return "young";
						} else {
							return "adult";
						}
					})));
			System.out.println(" personMap=" + personMap);
		}
	}

	static void test2() {
		List<String> lis = Arrays.asList("Hollis", "HollisChuang", "hollis", "Hello", "HelloWorld", "Hollis");
		Stream<String> stream = lis.stream();
		//
		int opt = 2;
		opt = 7;
		switch (opt) {
		case 1:
			log(1, stream.count());
		case 2:
			LongStream longS = stream.mapToLong(e -> 1L);
			List lis2 = Misc9a.listOfStream(longS);
			log(1, lis2);
			break;
		case 3:
			stream = stream.distinct();
			log(1, stream.count());
			break;
		case 4:
			Map<String, List<String>> map = new HashMap<>();
			map.put("a", lis);
			map.put("b", Arrays.asList("jj"));
			List<String> lis3 = map.values().stream().flatMap(e -> e.stream()).collect(Collectors.toList());
			log(1, lis3);
			lis3 = Misc9a.listOfStream(lis3.stream().distinct());
			log(1, lis3);
			break;
		case 5: {
			List<String> list = Arrays.asList("a", "b", null, null);
			List<String> list2 = list.stream().filter(Objects::nonNull).collect(Collectors.toList());
			log(1, list2);
			break;
		}
		case 6: {//consumer
			stream = Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
			final int[] inc = { 0 };
			stream.forEach((ele) -> {
				inc[0]++;
				log(1, ele);
			});
		}
		case 7: {//reduce
			BinaryOperator<String> op = new BinaryOperator<String>() {
				@Override
				public String apply(String t, String String) {
					// TODO Auto-generated method stub
					return null;
				}
			};
			Optional<Integer> accResult = Stream.of(33, 11, 44, 22).reduce((acc, item) -> {
				log(1, "acc : " + acc + "," + item);
				acc += item;
				//				log(1,"item: " + item);
				//				log(1,"acc+ : " + acc);
				//				log(1,"--------");
				return acc;
			});
			log(1, accResult);
		}
		case 8: {//Optional thream.findFirst()
			Object user = Arrays.asList("bb", "aa").stream().filter(ele -> ele.equals("aa")).findFirst().orElse("jj");
			log(1, user);
		}
		default:
			break;
		}
		System.exit(0);
	}

	static void test3() {
		{
			String st = Stream.of("a", "b", "c", "d").collect(Collectors.joining("#"));
			System.out.println(st);
		}
	}

	static class Person {
		String id;

		String name;

		int age;

		String type;

		public String getType() {
			return type;
		}

		public String getId() {
			return id;
		}

		public String getName() {
			return name;
		}

		public int getAge() {
			return age;
		}

		public Person(String id) {
			this.id = id;
		}

		public Person(String id, String name, int age) {
			this.id = id;
			this.name = name;
			this.age = age;
		}

		@Override
		public String toString() {
			return "id=" + id;// + ",type=" + type + ",name=" + name + ",age=" + age;
		}
	}

	static void collect_demo() {
		{
			/*
				  Supplier<R> supplier,
			  BiConsumer<R, ? super T> accumulator,
			  BiConsumer<R, R> combiner
			 */
			List<Person> list = Arrays.asList(new Person("i3"), new Person("i1"), new Person("i6"), new Person("i2"));
			Map<String, Person> personMap = list.stream().collect(//
					() -> new HashMap<>(), //
					(map, p) -> {
						map.put(p.getId(), p);
					}, //
					(m, n) -> {
						Ca.log(1, "jjj");
						m.putAll(n);
					});
			Ca.log(1, personMap);
		}
	}

	public static void main(String[] args) {
		int opt = 1;
		//		opt = 5;
		if (opt == 1) {
			groupingBy_test(1);
		} else if (opt == 2) {
			test3();
		} else if (opt == 3) {
			collect_demo();
		} else if (opt == 4) {
			flatMap();
		} else if (opt == 5) {
			misc_test(1);
		} else {
			test2();
		}
	}
}
